#include <linux/module.h>
#include <linux/param.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/uaccess.h>
#include <linux/sched.h>
#include <linux/wait.h>

#define DEVICE_NAME     "my-2440-button"
#define BUTTON_MAJOR    220

struct button_irq_desc {
    int irq;
    unsigned long flags;
    char *name;
};

static struct button_irq_desc btns[] = {
    {IRQ_EINT8,  IRQF_TRIGGER_FALLING, "myKEY0"},
    {IRQ_EINT11, IRQF_TRIGGER_FALLING, "myKEY1"},
    {IRQ_EINT13, IRQF_TRIGGER_FALLING, "myKEY2"},
    {IRQ_EINT14, IRQF_TRIGGER_FALLING, "myKEY3"},
    {IRQ_EINT15, IRQF_TRIGGER_FALLING, "myKEY4"},
    {IRQ_EINT19, IRQF_TRIGGER_FALLING, "myKEY5"},
};

static int press_count[] = {0, 0, 0, 0 ,0, 0};
static volatile int ev_press = 0;
static DECLARE_WAIT_QUEUE_HEAD(button_waitq);

irqreturn_t s3c2440_button_interrupt (int irq, void *data)
{
    volatile unsigned int *count = (volatile unsigned int*)data;

    *count += 1;
    ev_press = 1;
    wake_up_interruptible(&button_waitq);
    
    printk(KERN_INFO "%s success\n", __FUNCTION__);

    return IRQ_RETVAL(IRQ_HANDLED);
}

int s3c2440_open (struct inode *node, struct file *file)
{
    int i, err;

    for (i = 0; i < sizeof(btns) / sizeof(btns[0]); i++) {
        err = request_irq(btns[i].irq, s3c2440_button_interrupt, btns[i].flags,
                    btns[i].name, (void*)&press_count[i]);
        if (err) {
            printk(KERN_ERR "%s request_irq %d err %d %s", __FUNCTION__, i, err,
                    btns[i].name);
            break;
        }
    }

    if (err) {
        for (--i; i >= 0; --i) {
            free_irq(btns[i].irq, (void*)press_count[i]);
        }

        return -EBUSY;
    }
    
    printk(KERN_INFO "%s success\n", __FUNCTION__);

    return 0;
}

int s3c2440_release (struct inode *node, struct file *file)
{
    int i;
    
    for (i = 0; i < sizeof(btns) / sizeof(btns[0]); i++) {
        free_irq(btns[i].irq, (void*)press_count[i]);
    }

    printk(KERN_INFO "%s success\n", __FUNCTION__);

    return 0;
}

ssize_t s3c2440_read (struct file *file,
                      char __user *buf,
                      size_t len,
                      loff_t *off)
{
    unsigned long err;

    wait_event_interruptible(button_waitq, ev_press);
    ev_press = 0;

    err = copy_to_user(buf, (const char*)press_count, len);
    memset((void*)press_count, 0 , sizeof(press_count));

    printk(KERN_INFO "%s %u\n", __FUNCTION__, err);

    return err ? -EFAULT : 0;
}


ssize_t s3c2440_write (struct file *file,
                       const char __user *buf,
                       size_t len,
                       loff_t *off)
{
    printk(KERN_INFO "%s success\n", __FUNCTION__);

    return 0;
}


static struct file_operations btn_fops = {
    .owner   = THIS_MODULE,
    .open    = s3c2440_open,
    .release = s3c2440_release,
    .read    = s3c2440_read,
    .write   = s3c2440_write,
};


static int __init s3c2440_button_init (void)
{
    int rtn;
    
    rtn = register_chrdev(BUTTON_MAJOR, DEVICE_NAME, &btn_fops);
    if (rtn < 0) {
        printk(KERN_ERR "%s: register_chrdev error %d\n", __FUNCTION__, rtn);
        return -1;
    }
    
    printk(KERN_INFO "%s success\n", __FUNCTION__);

    return 0;
}

static void __exit s3c2440_button_exit (void)
{
    printk(KERN_INFO "%s\n", __FUNCTION__);
}

module_init(s3c2440_button_init);
module_exit(s3c2440_button_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("chzhou");

